﻿namespace NumericalData.Statistics
open NumericalData
open NumericalData.Systems
open NumericalData.Exceptions
open System.Diagnostics
open System
open System.Threading
open System.Collections.Generic
open GraphEntities
open NumericalData.SolvationMethods
///// {=========================StatisticsBasedOnSystemsDataBase=========================}
type SystemsSolvationsStatistics = 
    class
        ///-----------------------------Fields-----------------------------

        ///-----------------------------Constructors-----------------------------

        ///-----------------------------Properties-----------------------------

        ///-----------------------------Operators-----------------------------

        ///-----------------------------Methods-----------------------------
        
        static member GetStatisticsForFunctions (minDimention:int) 
            (maxDimention:int) (amountOfSystemsPerDimention:int) 
            (accuracy:double) (maxIterations:int) (startVectorIsZero:bool)
            (intermediateWatcher:Action<int,int,obj>) (refresher:obj)
            (methods:TraceableIterationMethod[]) = 
            SystemsSolvationsStatistics.GetStatisticsForTraceableIterationMethods minDimention maxDimention 
                amountOfSystemsPerDimention accuracy 
                methods maxIterations startVectorIsZero 
                MatrixGenerators<SquareMatrix>.GenerateMatrixWithDiagonalDominance 
                intermediateWatcher refresher
        
        static member AsyncGetStatisticsForFunctions (minDimention:int) 
            (maxDimention:int) (amountOfSystemsPerDimention:int) 
            (accuracy:double) (maxIterations:int) (startVectorIsZero:bool)
            (intermediateWatcher:Action<int,int,obj>) (refresher:obj)
            (methods:TraceableIterationMethod[]) 
            (asyncSuccess:Action<StatisticsContainer[]>) (asyncFail:Action<exn>) (asyncCancel:Action<OperationCanceledException>) = 
                let successClearFunc    = fun (stats:StatisticsContainer[]) ->
                                                asyncSuccess.Invoke(stats)
                let failClearFunc       = fun (exc:exn) ->
                                                asyncFail.Invoke(exc)
                let cancelClearFunc     = fun (exc:OperationCanceledException) ->
                                                asyncCancel.Invoke(exc)     

                let asyncGetStats (methods:TraceableIterationMethod[])  = 
                    let solve = 
                        new Func<(TraceableIterationMethod[]),StatisticsContainer[]>(
                            SystemsSolvationsStatistics.GetStatisticsForFunctions minDimention maxDimention amountOfSystemsPerDimention accuracy maxIterations startVectorIsZero intermediateWatcher refresher)
                    Async.FromBeginEnd(
                        (methods),
                        solve.BeginInvoke,
                        solve.EndInvoke)                
                let asyncOp = 
                    Async.TryCancelled(
                        (
                            async{
                                    let! res = asyncGetStats methods
                                    do! Async.Sleep(0)
                                    return res
                                }
                        ),cancelClearFunc)
                let cancellationSource = new CancellationTokenSource()                                
                Async.StartWithContinuations(   asyncOp,
                                                successClearFunc,
                                                failClearFunc,
                                                cancelClearFunc,
                                                cancellationSource.Token
                                            )
                cancellationSource

        static member private GetStatisticsForTraceableIterationMethods (minDim) (maxDim) (sysPerDim:int) (accur:double) 
            (methods:TraceableIterationMethod[]) (maxIterations:int) (startVectorIsZero:bool)
             (matrGenMethod:int->double->SquareMatrix) (intermediateWatcher:Action<int,int,obj>) (refresher:obj) = 

            let rec getAnswer sys accur startVectorIsZero maxIterations (solvMeth:TraceableIterationMethod)  = 
                let watch = new Stopwatch()
                watch.Start()
                try
                    let ans = solvMeth.TraceSolve sys accur startVectorIsZero maxIterations
                    watch.Stop()
                    ans
                with
                |exc ->
                    watch.Stop()
                    new DiagnosticsAnswer(new Answer(sys.InitialVector,maxIterations,100.,accur), watch.Elapsed, int64 1000000)
            
            let methodsAnswers = 
                [| 
                    for meth in 0..(methods.Length - 1) do 
                        yield (
                            new System.Collections.Generic.List<System.Collections.Generic.List<DiagnosticsAnswer>>( 
                                seq{
                                    for i in 0..(maxDim - minDim) do
                                        yield new System.Collections.Generic.List<DiagnosticsAnswer>()
                                })
                        ) 
                |]
            for dimIndex in 0..(maxDim - minDim) do
                for sysIndex in 0..(sysPerDim - 1) do 
                    let avg = (float sysIndex + 5.0)
                    let dimentions = dimIndex + minDim
                    let matr = matrGenMethod dimentions  avg
                    let vec = Vector.Generate dimentions avg
                    let system = new SLAE(matr,vec)
                    for methodIndex in 0..(methodsAnswers.Length - 1) do
                        let methodList = methodsAnswers.[methodIndex].[dimIndex]
                        let answer = getAnswer system accur startVectorIsZero maxIterations (methods.[methodIndex])
                        methodList.Add(answer)
                    intermediateWatcher.Invoke(dimentions,(sysIndex + 1),refresher)
            [| for i in 0..(methodsAnswers.Length - 1) do
                yield (MethodsStatistics.CalculateStatistics<DiagnosticsAnswer>(methodsAnswers.[i],methods.[i],minDim))
            |]

        ///-----------------------------InterfaceImplementation------------------


    end